﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Text.RegularExpressions;
using System.Xml;
using System.IO;
using System.Drawing;
using System.Collections.Specialized;
using System.Configuration;
using DotNETX.GIS.Web.StaticTMS.Classes;
using System.Xml.Serialization;
using DotNETX.Web.Handlers;

namespace DotNETX.GIS.Web.StaticTMS
{
    /// <summary>
    /// web.config/map.config配置说明
    /// 
    /// <Appsettings />
    /// 服务列表：
    /// DotNETX.GIS.Web.StaticTMSInstances eg:ter,map,sat
    /// 服务配置：
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName} *必填 eg:~/TileCache/ter
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.Relative
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.SRS
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.BBox
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.Levels
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.Origin
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.Resolutions
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.Size
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.Format
    /// DotNETX.GIS.Web.StaticTMS.{ServiceName}.Extention
    /// </summary>
    public class TMSHandler : BaseHandler
    {
        #region IHttpHandler 成员

        public override void ProcessRequest(HttpContext context)
        {
            string path = context.Request.Path;
            context.Response.ContentEncoding = Encoding.UTF8;

            string act = Request.QueryString["act"] == null ? "" : Request.QueryString["act"];
            switch (act.ToLower())
            {
                case "list":
                    RereadConfig();
                    FetchServices();
                    break;
                case "getservice":
                    RereadConfig();
                    FetchServiceSchema();
                    break;
                case "gettile":
                    FetchTileImage();
                    break;
                default:

                    break;
            }
        }

        #region ProcessRequest

        private void FetchServices()
        {
            Response.Clear();
            Response.ContentType = "text/xml";
            ResponseServices();
            Response.Flush();
            Response.End();
        }

        private void FetchServiceSchema()
        {
            string path = Request.Path;

            Response.Clear();
            Response.ContentType = "text/xml";
            ResponseServiceSchema();
            Response.Flush();
            Response.End();
        }

        private void FetchTileImage()
        {
            string serviceName = Request.QueryString["servicename"];
            string path = Request.QueryString["path"];

            string strFormat = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.Format", serviceName)];
            string mime = strFormat;

            System.Drawing.Imaging.ImageFormat imageformat = System.Drawing.Imaging.ImageFormat.Jpeg;
            if (strFormat != null)
                switch (strFormat.ToLower())
                {
                    case "png":
                        imageformat = System.Drawing.Imaging.ImageFormat.Png;
                        break;
                    case "gif":
                        imageformat = System.Drawing.Imaging.ImageFormat.Gif;
                        break;
                    case "jpg":
                    case "jpeg":
                        imageformat = System.Drawing.Imaging.ImageFormat.Jpeg;
                        mime = "jpeg";
                        break;
                    case "tif":
                    case "tiff":
                        imageformat = System.Drawing.Imaging.ImageFormat.Tiff;
                        break;
                    default:
                        imageformat = System.Drawing.Imaging.ImageFormat.Jpeg;
                        mime = "jpeg";
                        break;
                }
            else
            {
                mime = "jpeg";
            }
            Response.Clear();
            Response.ContentType = "image/" + mime;
            Response.ClearContent(); //需要输出图象信息 要修改HTTP头 
            Response.WriteFile(GetTilePhysicalPath(serviceName, path));
            Response.Flush();
            Response.End();
        }

        /// <summary>
        /// 仅当通过TMSHandler获取图片方式采用调用改方法获取图片物理路径
        /// </summary>
        /// <param name="serviceName"></param>
        /// <param name="catalog"></param>
        /// <returns></returns>
        protected string GetTilePhysicalPath(string serviceName, string catalog)
        {
            string folder = AppSettings["DotNETX.GIS.Web.StaticTMS." + serviceName];

            return string.Format("{0}{1}", folder == null ? "" : folder.Replace('/', '\\'), catalog == null ? "" : catalog.Replace('/', '\\')).Replace("\\\\", "\\");
        }
        #endregion

        public bool IsReusable
        {
            get { return false; }
        }

        #endregion

        #region 读取配置

        private static NameValueCollection ReadConfig()
        {
            NameValueCollection AppSettings = new NameValueCollection(System.Configuration.ConfigurationManager.AppSettings);
            XmlDocument doc = new XmlDocument();
            string mapconfig = HttpContext.Current.Server.MapPath("~/map.config");
            if(File.Exists(mapconfig))
            {
                doc.Load(mapconfig);
                XmlNodeList adds = doc.SelectNodes("//appSettings/add");
                foreach (XmlNode n in adds)
                {
                    AppSettings[n.Attributes["key"].Value] = n.Attributes["value"].Value;
                }
            }

            if (string.IsNullOrEmpty(AppSettings["DotNETX.GIS.Web.StaticTMSInstances"]))
            {
                using(Stream s = DotNETX.Utility.Resources.FileLoader.GetEmbededStream("map.config", typeof(TMSHandler)))
                {
                    doc.Load(s);
                    XmlNodeList adds = doc.SelectNodes("//appSettings/add");
                    foreach (XmlNode n in adds)
                    {
                        AppSettings[n.Attributes["key"].Value] = n.Attributes["value"].Value;
                    }
                }
            }

            return AppSettings;
        }

        private static void RereadConfig()
        {
            _appSettings = ReadConfig();
        }

        private static NameValueCollection _appSettings = null;
        private static NameValueCollection AppSettings
        {
            get
            {
                if (_appSettings == null)
                {
                    _appSettings = ReadConfig();
                }
                return _appSettings;

            }
        }

        #endregion

        protected void ResponseServices()
        {
            // 获取服务列表
            string[] services = null;
            string strServices = AppSettings["DotNETX.GIS.Web.StaticTMSInstances"];
            if (strServices != null)
            {
                services = strServices.Split(',');
            }

            int idx = Request.Url.AbsoluteUri.IndexOf('?');
            string baseUrl =  idx < 0 ? Request.Url.AbsoluteUri : Request.Url.AbsoluteUri.Remove(idx);

            Services Svr = new Services();
            foreach (string svrname in services)
            {
                Svr.Add(new TileMapService() { Title = svrname, Version = "1.0.0", Href = string.Format("{0}?act=getservice&servicename={1}", baseUrl, svrname) });
            }

            XmlSerializer xs = new XmlSerializer(typeof(Services));
            System.Xml.XmlWriter xw = new XmlTextWriter(Response.Output);
            XmlSerializerNamespaces n = new XmlSerializerNamespaces();
            n.Add("", "");
            xs.Serialize(xw, Svr, n);
        }

        protected void ResponseServiceSchema()
        {
            string serviceName = Request.QueryString["servicename"];
            int idx = Request.Url.AbsoluteUri.IndexOf('?');
            string baseUrl = idx < 0 ? Request.Url.AbsoluteUri : Request.Url.AbsoluteUri.Remove(idx);
            string serviceUrl = string.Format("{0}?act=gettile&servicename={1}&path=", baseUrl, serviceName);

            #region 解析配置

            double[] bbox = new double[] { -20037508.342789, -20037508.342789, 20037508.342789, 20037508.342789 };
            double[] origin = new double[] { -20037508.342789, 20037508.342789 };
            int startZ = 0;
            int endZ = 16;
            int width = 256;
            int height = 256;
            string format = "jpeg";
            string extention = "jpg";
            string srs = "EPSG:3785";

            double[] resolutions = new double[] { 
                156543.033900000, 78271.516950000, 39135.758475000, 19567.879237500, 
                9783.939618750, 4891.969809375, 2445.984904688, 1222.992452344, 
                611.496226172, 305.748113086, 152.874056543, 76.437028271, 
                38.218514136, 19.109257068, 9.554628534, 4.777314267, 
                2.388657133, 1.194328567, 0.597164283, 0.298582142, 
                0.149291071, 0.074645535, 0.037322768, 0.018661384, 
                0.009330692, 0.004665346, 0.002332673, 0.001166336, 
                0.000583168 };

            string folder = AppSettings["DotNETX.GIS.Web.StaticTMS." + serviceName];
            if (folder == null) throw new Exception(string.Format("未配置服务[{0}]", serviceName));

            string relative = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.Relative", serviceName)];

            string strLevels = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.Levels", serviceName)];
            if (strLevels != null)
            {
                string[] ls = strLevels.Split(',');
                int[] ils = new int[2];
                bool flag = true;
                try
                {
                    for (int i = 0; i < 2; i++)
                    {
                        ils[i] = int.Parse(ls[i]);
                    }
                }
                catch
                {
                    flag = false;
                }
                if (flag)
                {
                    startZ = ils[0];
                    endZ = ils[1];
                }
            }
            else
            {
                DirectoryInfo di = null;
                if (relative != null && (relative.Trim().ToLower() == "true" || relative.Trim() == "1"))
                {
                    di = new DirectoryInfo(HttpContext.Current.Server.MapPath(folder));
                }
                else
                {
                    di = new DirectoryInfo(folder);
                }
                DirectoryInfo[] levels = di.GetDirectories();
                int minlevel = int.MaxValue;
                int maxlevel = 0;
                int templevel = 0;
                for (int i = 0; i < levels.Length; i++)
                {
                    if (int.TryParse(levels[i].Name, out templevel))
                    {
                        if (minlevel > templevel) minlevel = templevel;
                        if (maxlevel < templevel) maxlevel = templevel;
                    }
                }
                if (minlevel != int.MaxValue)
                {
                    startZ = minlevel;
                    endZ = maxlevel;
                }
                else
                {
                    endZ = -1;
                }
            }
            string strSrs = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.SRS", serviceName)];
            if (strSrs != null)
            {
                srs = strSrs;
            }

            string strBBox = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.BBox", serviceName)];
            if (strBBox != null)
            {
                string[] bs = strBBox.Split(',');
                double[] dbs = new double[4];
                bool flag = true;
                try
                {
                    for (int i = 0; i < 4; i++)
                    {
                        dbs[i] = double.Parse(bs[i]);
                    }
                }
                catch
                {
                    flag = false;
                }
                if (flag)
                {
                    bbox = dbs;
                }
            }

            string strOrigin = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.Origin", serviceName)];
            if (strOrigin != null)
            {
                string[] os = strOrigin.Split(',');
                double[] dos = new double[2];
                bool flag = true;
                try
                {
                    for (int i = 0; i < 2; i++)
                    {
                        dos[i] = double.Parse(os[i]);
                    }
                }
                catch
                {
                    flag = false;
                }
                if (flag)
                {
                    origin = dos;
                }
            }

            string strResolutions = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.Resolutions", serviceName)];
            if (strResolutions != null)
            {
                string[] rs = strResolutions.Split(',');
                double[] drs = new double[rs.Length];
                bool flag = true;
                try
                {
                    for (int i = 0; i < rs.Length; i++)
                    {
                        drs[i] = double.Parse(rs[i]);
                    }
                }
                catch
                {
                    flag = false;
                }
                if (flag)
                {
                    resolutions = drs;
                }
            }

            string strSize = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.TileSize", serviceName)];
            if (strSize != null)
            {
                int tempSize = 256;
                if (int.TryParse(strSize, out tempSize) && tempSize > 0)
                {
                    width = height = tempSize;
                }
            }

            string strFormat = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.Format", serviceName)];
            if (strFormat != null)
            {
                format = strFormat;
                if (format == "jpg") format = "jpeg";
            }

            string strExtention = AppSettings[string.Format("DotNETX.GIS.Web.StaticTMS.{0}.Extention", serviceName)];
            if (strExtention != null)
            {
                extention = strExtention;
            }
            else
            {
                extention = format;
                if (format == "jpeg") extention = "jpg";
            }

            string tileDirectionary = serviceUrl;
            if (relative != null && (relative.Trim().ToLower() == "true" || relative.Trim() == "1"))
            {
                folder = folder.Replace("~/", "");
                if (folder[0] != '/') folder = "/" + folder;
                tileDirectionary = serviceUrl.Remove(serviceUrl.LastIndexOf("/tms.ashx")) + folder;
            }

            #endregion

            TileMap map = new TileMap()
            {
                Version = "1.0.0",
                Title = serviceName,
                Abstract = "",
                KeywordList = "",
                SRS = srs,
                BoundingBox = new BoundingBox() { MinX = bbox[0], MinY = bbox[1], MaxX = bbox[2], MaxY = bbox[3] },
                Origin = new Origin() { X = origin[0], Y = origin[1] },
                TileFormat = new TileFormat() { Width = width, Height = height, Extension = extention, MimeType = "image/" + format },
            };
            map.TileSets = new TileSet[endZ - startZ + 1];
            for (int i = startZ; i <= endZ; i++)
            {
                map.TileSets[i - startZ] = new TileSet() { UnitPerPixel = resolutions[i], Order = i - startZ, Href = string.Format("{0}/{1}", tileDirectionary, i) };
            }


            XmlSerializer xs = new XmlSerializer(typeof(TileMap));
            System.Xml.XmlWriter xw = new XmlTextWriter(Response.Output);
            XmlSerializerNamespaces n = new XmlSerializerNamespaces();
            n.Add("", "");
            xs.Serialize(xw, map, n);
        }

    }
}
